﻿using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Xsl;
using CSP.MembershipInterface.ObjectModel;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using System.Configuration;


namespace CSP.CRM.Integration
{
  public class CrmFacade
  {
      private CrmService crm;
      private MetadataService meta;
      private string cspUserAttribute = "csp_username";
      private CaseType[] caseTypes; // Case types cache
      private CaseStatus[] caseStatuses; // Case statuses cache
      private string xslDirectory = null;


      /// <summary>
      /// Create CRM SDK web service proxy with default credentials to the given CRM Url and organization
      /// </summary>
      /// <param name="baseCrmUrl"></param>
      /// <param name="organizationName"></param>
      /// <returns></returns>
      public static CrmFacade CreateCrmFacade(string baseCrmUrl, string organizationName)
      {
          CrmService crmSvc = new CrmService();
          crmSvc.CrmAuthenticationTokenValue = new CrmAuthenticationToken();
          crmSvc.CrmAuthenticationTokenValue.OrganizationName = organizationName;
          crmSvc.Url = baseCrmUrl + "/mscrmservices/2007/crmservice.asmx";
          crmSvc.UseDefaultCredentials = true;
          CrmFacade crm = new CrmFacade(crmSvc);
          return crm;
      }

      /// <summary>
      /// Create CRM SDK web service proxy with specific credentials to the given CRM Url and organization
      /// </summary>
      /// <param name="baseCrmUrl"></param>
      /// <param name="organizationName"></param>
      /// <param name="userName"></param>
      /// <param name="password"></param>
      /// <param name="domain"></param>
      /// <returns></returns>
      public static CrmFacade CreateCrmFacade(string baseCrmUrl, string organizationName, string userName, string password, string domain)
      {
          CrmService crmSvc = new CrmService();
          crmSvc.CrmAuthenticationTokenValue = new CrmAuthenticationToken();
          crmSvc.CrmAuthenticationTokenValue.OrganizationName = organizationName;
          crmSvc.Url = baseCrmUrl + "/mscrmservices/2007/crmservice.asmx";
          NetworkCredential cred = new NetworkCredential(userName, password, domain);
          crmSvc.Credentials = cred;
          CrmFacade crm = new CrmFacade(crmSvc);
          return crm;
      }

      /// <summary>
      /// Private constructor, called from the static Create methods
      /// </summary>
      /// <param name="crm"></param>
      private CrmFacade(CrmService crm)
      {
          this.crm = crm;
          meta = new MetadataService();
          Uri crmServerUri = new Uri(crm.Url);
          meta.Url = String.Format("{0}://{1}:{2}/mscrmservices/2007/metadataservice.asmx", crmServerUri.Scheme, crmServerUri.Host, crmServerUri.Port);
          meta.CrmAuthenticationTokenValue = new CrmAuthenticationToken();
          meta.CrmAuthenticationTokenValue.OrganizationName = crm.CrmAuthenticationTokenValue.OrganizationName;
          meta.Credentials = crm.Credentials;
          xslDirectory = Directory.GetCurrentDirectory();
      }

      /// <summary>
      /// Gets or sets the directory where the xsl transformations are stored.
      /// </summary>
      public string XslDirectory
      {
          get { return xslDirectory; }
          set { xslDirectory = value; }
      }

      /// <summary>
      /// Returns the list of available statuses for cases in the current 
      /// </summary>
      /// <returns></returns>
      public CaseStatus[] GetCaseStatuses()
      {
          if (caseStatuses == null)
              BuildCaseStatusList();
          return caseStatuses;
      }


      /// <summary>
      /// Gets all active cases sorted by descending ticket number. 
      /// TODO: Retrieves only case information, contact information (requeter adress etc) is missing
      /// </summary>
      /// <returns>Collection of incidents</returns>
      public Case[] GetCases()
      {
          return GetCases(1);
      }

      /// <summary>
      /// Get all cases with the given status, sorted by descending ticket number
      /// </summary>
      /// <param name="caseStatus"></param>
      /// <returns></returns>
      public Case[] GetCasesByCaseStatus(CaseStatus caseStatus)
      {
          switch (caseStatus.CaseStatusId)
          {
              case 1: return GetCases(1);
              case 2: return GetCases(2);
              default: return GetCases();
          }
      }

      /// <summary>
      /// TODO: returns all open cases
      /// </summary>
      /// <param name="caseRoleType"></param>
      /// <returns></returns>
      public Case[] GetCasesByCaseRoleType(CaseRoleType caseRoleType)
      {
          return GetCases(1);
      }


      public Case[] GetCasesByCaseType(CaseType caseType)
      {
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "statuscode", "csp_proceduretype" };
          q.Values = new string[] { "1", caseType.CaseTypeId.ToString() };
          q.EntityName = EntityName.incident.ToString();
          q.ColumnSet = new AllColumns();
          OrderExpression oe = new OrderExpression();
          oe.AttributeName = "ticketnumber";
          oe.OrderType = OrderType.Descending;
          q.Orders = new ArrayList();
          q.Orders.Add(oe);

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCases = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCases == 0)
              return new Case[] { };
          int i = 0;
          Case[] casesArray = new Case[numberOfCases];

          foreach (DynamicEntity crmCase in resp.BusinessEntityCollection.BusinessEntities)
              casesArray[i++] = Util.CreateFromCrmCase(crmCase);
          return casesArray;

      }


      /// <summary>
      /// Get cases for a citizen
      /// </summary>
      /// <param name="username"></param>
      /// <returns></returns>
      public Case[] GetCasesByCitizenID(string username)
      {
          // Retrieve the Guid of the citizen
          Guid contactId = GetContactIdByCspUser(username);
          if (contactId == Guid.Empty)
              throw new InvalidOperationException(String.Format("Citizen '{0}' not found", username));

          // Fetch the active CSP cases belonging to the citizen
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "statuscode", "customerid", "casetypecode" };
          q.Values = new string[] { "1", contactId.ToString(), "200555" };
          q.EntityName = EntityName.incident.ToString();
          q.ColumnSet = new AllColumns();
          OrderExpression oe = new OrderExpression();
          oe.AttributeName = "ticketnumber";
          oe.OrderType = OrderType.Descending;
          q.Orders = new ArrayList();
          q.Orders.Add(oe);

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCases = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCases == 0)
              return new Case[] { };

          // Convert the CRM types to CRMConnector types
          List<Case> casesList = new List<Case>();
          foreach (DynamicEntity crmCase in resp.BusinessEntityCollection.BusinessEntities)
          {
              Case newCase = Util.CreateFromCrmCase(crmCase);
              if (newCase != null)
                  casesList.Add(newCase);
          }
          return casesList.ToArray();
      }


      private Case[] GetCases(int statuscode)
      {
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "statuscode", "casetypecode" };
          q.Values = new string[] { statuscode.ToString(), "200555" };
          q.EntityName = EntityName.incident.ToString();
          q.ColumnSet = new AllColumns();
          OrderExpression oe = new OrderExpression();
          oe.AttributeName = "ticketnumber";
          oe.OrderType = OrderType.Descending;
          q.Orders = new ArrayList();
          q.Orders.Add(oe);

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCases = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCases == 0)
              return new Case[] { };
          int i = 0;
          Case[] casesArray = new Case[numberOfCases];

          foreach (DynamicEntity crmCase in resp.BusinessEntityCollection.BusinessEntities)
              casesArray[i++] = Util.CreateFromCrmCase(crmCase);
          return casesArray;
      }

      /// <summary>
      /// Fetches a case using its case identifier
      /// </summary>
      /// <param name="caseID"></param>
      /// <returns></returns>
      public Case GetCaseByCaseID(string caseID)
      {
          //TODO: tignatov DASB 16/01/2009
          // Test change ticketnumber -> incidentid
          // Shouldn't be called when using CrmConnector

          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "incidentid" };
          q.Values = new string[] { caseID };
          q.EntityName = EntityName.incident.ToString();
          q.ColumnSet = new AllColumns();

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCases = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCases == 0)
              return null;

          DynamicEntity crmCase = (DynamicEntity)resp.BusinessEntityCollection.BusinessEntities[0];
          return Util.CreateFromCrmCase(crmCase);
      }

      /// <summary>
      /// Updates citizen satisfaction for a case.
      /// 5 = Very satisfied
      /// 4 = Satisfied
      /// 3 = Neutral
      /// 2 = Disatisfied
      /// 1 = Very disatisfied
      /// </summary>
      /// <param name="caseID"></param>
      /// <param name="satisfaction"></param>
      public void UpdateCaseSatisfaction(string caseID, int satisfaction)
      {
          //TODO: tignatov DASB 16/01/2009
          // Test change ticketnumber -> incidentid
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "incidentid" };
          q.Values = new string[] { caseID };
          q.EntityName = EntityName.incident.ToString();
          q.ColumnSet = new ColumnSet();

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;

          BusinessEntityCollection res = crm.RetrieveMultiple(q);
          if (res.BusinessEntities.Count == 0)
              return;
          incident crmCase = (incident)res.BusinessEntities[0];
          Guid crmCaseId = crmCase.incidentid.Value;
          crmCase.customersatisfactioncode = new Picklist(satisfaction);
          crm.Update(crmCase);
      }

      /// <summary>
      /// Creates a new procedure for the citizen using the data in the InfoPath form
      /// </summary>
      /// <param name="infoPathXml"></param>
      /// <returns></returns>
      public string CreateProcedure(string infoPathXml)
      {
          string dynamicEntityXml = null;
          try
          {
              XmlDocument infoPathDom = new XmlDocument();
              infoPathDom.LoadXml(infoPathXml);

              XmlNamespaceManager mgr = new XmlNamespaceManager(infoPathDom.NameTable);
              mgr.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2006-12-14T10:15:01");

              // Extract citizen identifier and procedure type from InfoPath data
              string cspUser, procedureName;

              string requestTypeXpath = "my:myFields/my:RequestInformation/my:RequestType";
              XmlNode requestTypeNode = infoPathDom.SelectSingleNode(requestTypeXpath, mgr);
              if (requestTypeNode != null)
                  procedureName = requestTypeNode.FirstChild.Value;
              else
                  throw new InvalidOperationException("RequestType not found on InfoPath data");

              string cspUserXpath = "my:myFields/my:Citizen/my:CitizenID";
              XmlNode cspUserNode = infoPathDom.SelectSingleNode(cspUserXpath, mgr);
              if (cspUserNode != null)
                  cspUser = cspUserNode.FirstChild.Value;
              else
                  throw new InvalidOperationException("CitizenID not found on InfoPath data");

              // Look up citizen in CRM
              ColumnSet cs = new ColumnSet();
              cs.AddColumn("fullname");
              DynamicEntity citizen = GetContactByUserName(cspUser, cs);
              if (citizen == null)
                  throw new InvalidOperationException(String.Format("Citizen with {0}='{1}' was not found", cspUserAttribute, cspUser));
              Guid contactId = new Guid(Util.GetKey(citizen, "contactid"));

              // Conversion InfoPath -> DynamicEntity      
              XslCompiledTransform xsl = new XslCompiledTransform();
              string xslFilePath = String.Format(@"{0}\{1}.xsl", xslDirectory, procedureName);
              xsl.Load(xslFilePath);


              MemoryStream ms = new MemoryStream();
              xsl.Transform(infoPathDom, null, ms);
              ms.Position = 0;

#if DEBUG
              StreamReader sr = new StreamReader(ms);
              dynamicEntityXml = sr.ReadToEnd();
              ms.Position = 0;
#endif

              // Deserialize DynamicEntity XML
              XmlSerializer xmlSerializer = new XmlSerializer(typeof(DynamicEntity));
              DynamicEntity procedure = (DynamicEntity)xmlSerializer.Deserialize(ms);

              // Fill in the citizen (contact) identificator
              LookupProperty contactProp = new LookupProperty("csp_contactid", new Lookup("contact", contactId));
              procedure.Properties.Add(contactProp);

              // Fill in the procedure title as the name of the procedure and citizen
              string citizenFullName = Util.GetStringProp(citizen, "fullname");
              string procedureTitle = String.Format("{0} - {1}", GetProcedureDisplayName(procedureName), citizenFullName);

              StringProperty titleProp = new StringProperty("csp_title", procedureTitle);
              procedure.Properties.Add(titleProp);

              // Call the CRM service to create the new procedure
              TargetCreateDynamic tcd = new TargetCreateDynamic();
              tcd.Entity = procedure;
              CreateRequest req = new CreateRequest();
              req.Target = tcd;
              CreateResponse resp = (CreateResponse)crm.Execute(req);

              Guid procedureId = resp.id;
              string ticketNumber = GetCaseTicketNumber(procedureName, procedureId);
              if (ticketNumber != null) // Case created correctly by the Plug-In
                  return ticketNumber;
              else
                  return null;
          }
          catch (SoapException soapEx)
          {
              return null;
          }
          catch (Exception ex)
          {
              return null;
          }
      }


      /// <summary>
      /// Gets the citizen with the given user name. Returns null if not found
      /// </summary>
      /// <param name="userName"></param>
      /// <returns></returns>
      public Citizen GetCitizenByUsername(string userName)
      {
          DynamicEntity contact = GetContactByUserName(userName, new AllColumns());
          if (contact == null)
              return null;
          return Util.CreateCitizenFromCrmContact(contact);
          //Citizen citizen = new Citizen();
          //citizen.Address = Util.GetStringProp(contact, "address1_line1");
          //citizen.Children = Util.GetNumberProp(contact, "numberofchildren");
          //citizen.City = Util.GetStringProp(contact, "address1_city");
          //citizen.DisplayName = Util.GetStringProp(contact, "fullname");
          //citizen.ElderlyRelatives = Util.GetNumberProp(contact, "csp_elderlyrelatives");
          //citizen.EmailAddress = Util.GetStringProp(contact, "emailaddress1");
          //citizen.FirstName = Util.GetStringProp(contact, "firstname");
          //citizen.LastName = Util.GetStringProp(contact, "lastname");
          //citizen.Phone = Util.GetStringProp(contact, "telephone1");
          //citizen.PostalCode = Util.GetStringProp(contact, "address1_postalcode");
          //citizen.Username = Util.GetStringProp(contact, "csp_username");
          //citizen.PhotoUrl = Util.GetStringProp(contact, "csp_picture");
          //return citizen;
      }


      /// <summary>
      /// Returns all the active contacts with csp_enabled = true
      /// </summary>
      /// <returns></returns>
      public Citizen[] GetCitizens()
      {
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "statuscode", "csp_enabled" };
          q.Values = new object[] { 1, true };
          q.EntityName = EntityName.contact.ToString();
          q.ColumnSet = new AllColumns();
          OrderExpression oe = new OrderExpression();
          oe.AttributeName = "lastname";
          oe.OrderType = OrderType.Ascending;
          q.Orders = new ArrayList();
          q.Orders.Add(oe);

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCitizens = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCitizens == 0)
              return new Citizen[] { };
          int i = 0;
          Citizen[] citizensArray = new Citizen[numberOfCitizens];
          foreach (DynamicEntity citizen in resp.BusinessEntityCollection.BusinessEntities)
              citizensArray[i++] = Util.CreateCitizenFromCrmContact(citizen);
          return citizensArray;
      }

      /// <summary>
      /// Returns all the active contacts with csp_enabled = false
      /// 
      /// </summary>
      /// <returns></returns>
      public CitizenRequest[] GetCitizenRequests()
      {
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "statuscode", "csp_enabled" };
          q.Values = new object[] { 1, false };
          q.EntityName = EntityName.contact.ToString();
          q.ColumnSet = new AllColumns();
          OrderExpression oe = new OrderExpression();
          oe.AttributeName = "lastname";
          oe.OrderType = OrderType.Ascending;
          q.Orders = new ArrayList();
          q.Orders.Add(oe);

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCitizens = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCitizens == 0)
              return new CitizenRequest[] { };
          List<CitizenRequest> citizenRequestList = new List<CitizenRequest>();
          foreach (DynamicEntity citizen in resp.BusinessEntityCollection.BusinessEntities)
          {
              CitizenRequest citReq = Util.CreateCitizenRequestFromCrmContact(citizen);
              if (citReq != null)
                  citizenRequestList.Add(citReq);
          }
          return citizenRequestList.ToArray();
      }

      /// <summary>
      /// Returns the citizen request with the given user name or null if not found
      /// </summary>
      /// <returns></returns>
      public CitizenRequest GetCitizenRequestByUserName(string userName)
      {
          QueryByAttribute q = new QueryByAttribute();
          q.Attributes = new string[] { "statuscode", "csp_enabled", "csp_username" };
          q.Values = new object[] { 1, false, userName };
          q.EntityName = EntityName.contact.ToString();
          q.ColumnSet = new AllColumns();
          OrderExpression oe = new OrderExpression();
          oe.AttributeName = "lastname";
          oe.OrderType = OrderType.Ascending;
          q.Orders = new ArrayList();
          q.Orders.Add(oe);

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.Query = q;
          req.ReturnDynamicEntities = true;
          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          int numberOfCitizens = resp.BusinessEntityCollection.BusinessEntities.Count;
          if (numberOfCitizens == 0)
              return null;
          DynamicEntity citizen = (DynamicEntity)resp.BusinessEntityCollection.BusinessEntities[0];
          return Util.CreateCitizenRequestFromCrmContact(citizen);
      }

      private string GetProcedureDisplayName(string procedureSchemaName)
      {
          if (caseTypes == null)
              BuildCaseTypeList();
          foreach (CaseType caseType in caseTypes)
              if (caseType.CaseTypeCode == procedureSchemaName)
                  return caseType.CaseTypeTitle;
          return null;
      }

      /// <summary>
      /// Returns the CaseType with the given name
      /// </summary>
      /// <param name="name"></param>
      /// <returns></returns>
      public CaseType GetCaseTypeByName(string procedureSchemaName)
      {
          if (caseTypes == null)
              BuildCaseTypeList();
          foreach (CaseType caseType in caseTypes)
              if (caseType.CaseTypeCode == procedureSchemaName)
                  return caseType;
          return null;
      }

      /// <summary>
      /// Accesses CRM Metadata service to fetch all case types: custom entities with
      /// schema names beginning with the csp prefix
      /// </summary>
      /// <returns></returns>
      public CaseType[] GetCaseTypes()
      {
          if (caseTypes == null)
              BuildCaseTypeList();
          return caseTypes;
      }


      /// <summary>
      /// Builds the caseType list with the available procedures in CRM by searching
      /// schema names beginning with the csp prefix. Used by GetCaseTypes() and GetProcedureDisplayName()
      /// </summary>
      /// <returns></returns>
      private void BuildCaseTypeList()
      {
          List<CaseType> caseTypeList = new List<CaseType>();
          // Retrieve all entity metadata
          RetrieveAllEntitiesRequest req = new RetrieveAllEntitiesRequest();
          // Attribute metadata is not needed
          req.MetadataItems = MetadataItems.EntitiesOnly;
          RetrieveAllEntitiesResponse resp = (RetrieveAllEntitiesResponse)meta.Execute(req);
          foreach (EntityMetadata emd in resp.CrmMetadata)
              if (emd.SchemaName.StartsWith("csp_"))
              {
                  // Build the CrmConnector CaseType object
                  CaseType caseType = new CaseType();
                  caseType.CaseTypeCode = emd.SchemaName;
                  caseType.CaseTypeTitle = emd.DisplayName.UserLocLabel.Label;
                  caseType.CaseTypeId = emd.ObjectTypeCode.Value;
                  caseTypeList.Add(caseType);
              }
          caseTypes = caseTypeList.ToArray();
      }

      /// <summary>
      /// </summary>
      /// <returns></returns>
      private void BuildCaseStatusList()
      {
          List<CaseStatus> caseStatusList = new List<CaseStatus>();

          RetrieveEntityRequest req = new RetrieveEntityRequest();
          req.EntityItems = EntityItems.IncludeAttributes;
          req.LogicalName = EntityName.incident.ToString();
          RetrieveEntityResponse resp = (RetrieveEntityResponse)meta.Execute(req);

          StatusAttributeMetadata smd = null;
          foreach (AttributeMetadata amd in resp.EntityMetadata.Attributes)
              if (amd.SchemaName == "StatusCode")
              {
                  smd = (StatusAttributeMetadata)amd;
                  break;
              }
          if (smd == null)
              throw new InvalidOperationException("Status attribute not found");
          foreach (Option option in smd.Options)
          {
              CaseStatus cs = new CaseStatus();
              cs.CaseStatusTitle = option.Label.UserLocLabel.Label;
              cs.CaseStatusId = option.Value.Value;
              caseStatusList.Add(cs);
          }
          caseStatuses = caseStatusList.ToArray();
      }

      /// <summary>
      /// Creates a new contact with the citizen info
      /// </summary>
      /// <param name="citizenRequest"></param>
      public void AddCitizenRequest(CitizenRequest citizenRequest)
      {
          if (citizenRequest.Username == null)
              throw new InvalidOperationException("User name cannot be null");
          DynamicEntity citizen = null;

          Request crmRequest = null;
          if (citizenRequest.RequestType == CitizenRequestType.Update)
          {
              citizen = GetContactByUserName(citizenRequest.Username, new AllColumns());
              CreateCitizenModifiedTask(citizen);
              TargetUpdateDynamic target = new TargetUpdateDynamic();
              target.Entity = citizen;
              UpdateRequest updateReq = new UpdateRequest();
              updateReq.Target = target;
              crmRequest = updateReq;
          }
          else if (citizenRequest.RequestType == CitizenRequestType.Add)
          {
              if (GetContactIdByCspUser(citizenRequest.Username) != Guid.Empty)
                  throw new InvalidOperationException(String.Format("Username '{0}' already in use", citizenRequest.Username));
              citizen = new DynamicEntity("contact");
              TargetCreateDynamic target = new TargetCreateDynamic();
              target.Entity = citizen;
              CreateRequest createReq = new CreateRequest();
              createReq.Target = target;
              crmRequest = createReq;
          }

          // Standard CRM properties
          Util.AddStringProp(citizen, "firstname", citizenRequest.FirstName);
          Util.AddStringProp(citizen, "lastname", citizenRequest.LastName);
          Util.AddStringProp(citizen, "emailaddress1", citizenRequest.EmailAddress);
          Util.AddStringProp(citizen, "address1_line1", citizenRequest.Address);
          Util.AddStringProp(citizen, "address1_city", citizenRequest.City);
          Util.AddStringProp(citizen, "address1_postalcode", citizenRequest.PostalCode);
          Util.AddStringProp(citizen, "address1_telephone1", citizenRequest.Phone);
          Util.AddStringProp(citizen, "telephone1", citizenRequest.Phone);
          Util.AddNumberProp(citizen, "numberofchildren", citizenRequest.Children);

          //// CSP properties
          Util.AddStringProp(citizen, "csp_username", citizenRequest.Username);
          Util.AddStringProp(citizen, "csp_password", citizenRequest.Password);
          Util.AddNumberProp(citizen, "csp_elderlyrelatives", citizenRequest.ElderlyRelatives);
          Util.AddStringProp(citizen, "csp_picture", citizenRequest.PhotoUrl);

          crm.Execute(crmRequest);
      }

      private void CreateCitizenModifiedTask(DynamicEntity citizen)
      {

          Guid contactId = new Guid(Util.GetKey(citizen, "contactid"));
          task task = new task();
          task.subject = ConfigurationSettings.AppSettings["CSP_ReviewDataTaskTitle"] ?? "Review citizen update";
          task.regardingobjectid = new Lookup(EntityName.contact.ToString(), contactId);

          string previousDataHeader = ConfigurationSettings.AppSettings["CSP_PreviousDataHeader"] ?? "Previous data";
          StringBuilder sb = new StringBuilder(previousDataHeader + "\r\n");
          List<string> attributes = new List<string>(new string[] { "firstname", "lastname",  "emailaddress1", 
      "address1_line1", "address1_city", "address1_postalcode", "address1_telephone1", 
      "telephone1", "numberofchildren", "csp_elderlyrelatives" });

          RetrieveEntityRequest req = new RetrieveEntityRequest();
          req.EntityItems = EntityItems.IncludeAttributes;
          req.LogicalName = EntityName.contact.ToString();
          RetrieveEntityResponse resp = (RetrieveEntityResponse)meta.Execute(req);

          string displayName = null, value = null;
          foreach (AttributeMetadata amd in resp.EntityMetadata.Attributes)
          {
              value = null;
              string attributeName = amd.SchemaName.ToLower();
              if (attributes.Contains(attributeName))
              {
                  displayName = amd.DisplayName.UserLocLabel.Label;
                  if ((amd is StringAttributeMetadata) && citizen.Properties.Contains(attributeName))
                      value = (string)citizen.Properties[attributeName];
                  else if ((amd is IntegerAttributeMetadata) && citizen.Properties.Contains(attributeName))
                      value = ((CrmNumber)citizen.Properties[attributeName]).formattedvalue;
                  else if ((amd is DecimalAttributeMetadata) && citizen.Properties.Contains(attributeName))
                      value = ((CrmDecimal)citizen.Properties[attributeName]).formattedvalue;
                  if (value != null)
                      sb.AppendFormat("{0}: {1}\r\n", displayName, value);
              }
          }

          task.description = sb.ToString();
          crm.Create(task);
      }


      /// <summary>
      /// Sets the csp_enabled attribute on the contact to true
      /// </summary>
      /// <param name="username"></param>
      public void ApproveCitizenRequest(string userName)
      {
          ColumnSet cs = new ColumnSet();
          cs.AddColumn("csp_enabled");
          DynamicEntity contact = GetContactByUserName(userName, cs);
          CrmBoolean cspEnabled = (CrmBoolean)contact.Properties["csp_enabled"];
          if ((cspEnabled != null) && (cspEnabled.Value)) // Already enabled
              return;

          CrmBooleanProperty cspEnabledProp = new CrmBooleanProperty("csp_enabled", new CrmBoolean(true));
          contact.Properties.Add(cspEnabledProp);

          TargetUpdateDynamic target = new TargetUpdateDynamic();
          target.Entity = contact;
          UpdateRequest updateReq = new UpdateRequest();
          updateReq.Target = target;
          crm.Execute(updateReq);
      }

      /// <summary>
      /// Deletes the contact only if csp_enabled is false. Throws exception else
      /// </summary>
      /// <param name="userName"></param>
      public void DenyCitizenRequest(string userName)
      {
          ColumnSet cs = new ColumnSet();
          cs.AddColumn("csp_enabled");
          DynamicEntity contact = GetContactByUserName(userName, cs);
          if (contact == null)
              throw new InvalidOperationException("Could not delete citizen '{0}' - Not found");

          CrmBoolean cspEnabled = (CrmBoolean)contact.Properties["csp_enabled"];
          if ((cspEnabled != null) && (cspEnabled.Value)) // Do not delete if enabled
              throw new InvalidOperationException("Could not delete citizen '{0}' because it has been approved");
          Key kContactId = (Key)contact.Properties["contactid"];
          Guid contactId = kContactId.Value;
          crm.Delete("contact", contactId);
      }


      private DynamicEntity GetContactByUserName(string userName, ColumnSetBase cs)
      {
          QueryByAttribute q = new QueryByAttribute();
          q.EntityName = EntityName.contact.ToString();
          q.Attributes = new string[] { "csp_username" };
          q.Values = new string[] { userName };
          q.ColumnSet = cs;

          RetrieveMultipleRequest req = new RetrieveMultipleRequest();
          req.ReturnDynamicEntities = true;
          req.Query = q;

          RetrieveMultipleResponse resp = (RetrieveMultipleResponse)crm.Execute(req);
          if (resp.BusinessEntityCollection.BusinessEntities.Count == 0)
              return null;
          else
              return (DynamicEntity)resp.BusinessEntityCollection.BusinessEntities[0];
      }

      private Guid GetContactIdByCspUser(string userName)
      {
          QueryByAttribute q = new QueryByAttribute();
          q.EntityName = EntityName.contact.ToString();
          q.Attributes = new string[] { cspUserAttribute };
          q.Values = new string[] { userName };
          q.ColumnSet = new ColumnSet();
          q.ColumnSet.AddColumn("fullname");
          BusinessEntityCollection result = crm.RetrieveMultiple(q);
          if (result.BusinessEntities.Count == 0)
              return Guid.Empty;
          if ((result.BusinessEntities == null) || (result.BusinessEntities.Count != 1))
              throw new InvalidOperationException(String.Format("Citizen identifier '{0}' not unique", userName));
          contact contact = ((contact)result.BusinessEntities[0]);
          return contact.contactid.Value;
      }


      private string GetCaseTicketNumber(string procedureName, Guid procedureId)
      {
          string fetchXml = @"<fetch mapping='logical' count='1'>
        <entity name='{0}'>
          <filter>
            <condition attribute='{0}id' operator='eq' value='{1}' />
          </filter>
          <link-entity name='incident' from='incidentid' to='csp_incidentid'>
            <attribute name='ticketnumber' />
          </link-entity>
        </entity>
      </fetch>";
          fetchXml = String.Format(fetchXml, procedureName, procedureId);
          string result = crm.Fetch(fetchXml);
          XmlDocument resultDom = new XmlDocument();
          resultDom.LoadXml(result);
          XmlNode ticketNumberNode = resultDom.SelectSingleNode("/resultset/result/csp_incidentid.ticketnumber");
          if (ticketNumberNode != null)
              return ticketNumberNode.FirstChild.Value;
          else
              return null;
      }

  }
}
